import * as THREE from 'three';

const cube = new THREE.BoxGeometry(1, 1, 1);

let loader = new THREE.TextureLoader();

function loadTexture(url) {
  const tex = loader.load(url);
  tex.wrapS = THREE.RepeatWrapping;
  tex.wrapT = THREE.RepeatWrapping;
  tex.repeat.set(1, 1);
  return tex;
}

const textures = {
  'grass': loadTexture('textures/grass.png'),
  'residential1': loadTexture('textures/residential1.png'),
  'residential2': loadTexture('textures/residential2.png'),
  'residential3': loadTexture('textures/residential3.png'),
  'commercial1': loadTexture('textures/commercial1.png'),
  'commercial2': loadTexture('textures/commercial2.png'),
  'commercial3': loadTexture('textures/commercial3.png'),
  'industrial1': loadTexture('textures/industrial1.png'),
  'industrial2': loadTexture('textures/industrial2.png'),
  'industrial3': loadTexture('textures/industrial3.png'),
};

function getTopMaterial() {
  return new THREE.MeshLambertMaterial({ color: 0x555555 });
}

function getSideMaterial(textureName) {
  return new THREE.MeshLambertMaterial({ map: textures[textureName].clone() });
}

function BuildingAsset(h) {
  return function(x, y) {
    const material = new THREE.MeshLambertMaterial({ color: 0x777777 });
    const mesh = new THREE.Mesh(cube, material);
    mesh.userData = { id: `building-${h}`, x, y };
    mesh.scale.set(1, h, 1);
    mesh.position.set(x, h * .5, y);
    return mesh;
  }
}

/**
 * @type {Record<import('./type').AssetId, THREE.Mesh<THREE.BoxGeometry, THREE.MeshLambertMaterial, THREE.Object3DEventMap>>}
 */
const assets = Object.assign(
  {
    'ground': (x, y) => {
      const material = new THREE.MeshLambertMaterial({ map: textures.grass });
      const mesh = new THREE.Mesh(cube, material);
      mesh.userData = { x, y };
      mesh.position.set(x, -.5, y);
      mesh.receiveShadow = true;
      return mesh;
    },
    'grass': (x, y) => {
      const material = new THREE.MeshLambertMaterial({ color: 0x00aa00 });
      const mesh = new THREE.Mesh(cube, material);
      mesh.userData = { id: 'grass', x, y };
      mesh.position.set(x, -.5, y);
      return mesh;
    },
    'bulldoze': (x, y) => {},
    'residential': createZoneMesh,
    'commercial': createZoneMesh,
    'industrial': createZoneMesh,
    'road': (x, y) => {
      const material = new THREE.MeshLambertMaterial({ color: 0x222222 });
      const mesh = new THREE.Mesh(cube, material);
      mesh.userData = { x, y };
      mesh.scale.set(1, .02, 1);
      mesh.position.set(x, .01, y);
      mesh.receiveShadow = true;
      return mesh;
    },
  },
  Array
  .from({ length: 3 }, (n, i) => i + 1)
  .reduce(
    (acc, n) => Object.assign(acc, { [`building-${n}`]: BuildingAsset(n) }),
    {}
  )
);

/**
 * 3D 资源
 * @param {import('./type').AssetId} assetId 
 * @param {number} x 
 * @param {number} y 
 * @param {*} data 
 */
export function createAssetInstance(assetId, x, y, data) {
  if (Reflect.has(assets, assetId)) {
    return assets[assetId](x, y, data);
  } else {
    console.log(`Asset Id ${assetId} is not found.`);
    return void 0;
  }
}

function createZoneMesh(x, y, data) {
  const textureName = data.type.concat(data.style);

  const topMaterial = getTopMaterial();
  const sideMaterial = getSideMaterial(textureName);
  let materialArray = [
    sideMaterial, // +x
    sideMaterial, // -x
    topMaterial,  // +x
    topMaterial,  // -x
    sideMaterial, // +z
    sideMaterial, // -z
  ];
  let mesh = new THREE.Mesh(cube, materialArray);
  mesh.userData = { x, y };
  mesh.scale.set(.8, (data.height - .95) * .5, .8);
  mesh.material.forEach(material => material.map?.repeat.set(1, data.height - 1));
  mesh.position.set(x, (data.height - .95) * .25, y);
  mesh.castShadow = true;
  mesh.receiveShadow = true;
  return mesh;
}
